#pragma once

#include <GL/glew.h>
#include <QGLWidget>

#include "../Core/DCoordinates3.h"
#include "../Core/TriangulatedMeshes3.h"

#include "../Cyclic/CyclicCurves3.h"
#include "../Core/GenericCurves3.h"

#include "../Core/Matrices.h"

#include "../Trigonometric/TrigonometricBernsteinSurfaces3.h"
#include "../Trigonometric/TrigoBernsteinSurfPopulation.h"

#include <QString>
#include <QTimer>
#include <QMessageBox>

namespace cagd
{
    class GLWidget: public QGLWidget
    {
        // ever class that provides signals and slots mechanism needs to define the
        // Q_OBJECT macro
        Q_OBJECT

    protected:
        // parameters of the perspective projection matrix
        GLfloat _aspect, _fovy, _z_near, _z_far;

        // parameters of the modelview matrix
        DCoordinate3 _eye, _center, _up;

        // geometry
        TriangulatedMesh3 _mouse;

        // transformation parameters
        GLdouble _scale;
        GLdouble _trans_x, _trans_y, _trans_z;
        GLdouble _angle_x, _angle_y, _angle_z;

        // (interpolating) cyclic curves
        GLuint          _n;
        GLuint          _m;
        CyclicCurve3    *_cc;

        GLdouble        _alpha;
        GLdouble        _beta;

        GLuint          _div_point_count, _max_order_of_derivatives;
        GenericCurve3   *_image_cc;

        CyclicCurve3    *_icc;
        GenericCurve3   *_image_icc;

        GLuint          _individual_number;
        GLuint          _individual_number_to_show;
        GLuint          _data_point_element_u_index;
        GLuint          _data_point_element_v_index;

        ColumnMatrix<GLdouble>      _knot_vector;
        ColumnMatrix<DCoordinate3>  _data_points_to_interpolate;

        // trigonometric Bernstein-like tensor product surfaces
        TrigonometricBernsteinSurface3  *_tbs;
        TriangulatedMesh3               *_image_tbs;


        // interpolating trigonometric Bernstein-like tensor product surfaces
        TrigonometricBernsteinSurface3  *_itbs;
        TriangulatedMesh3               *_image_itbs;

        RowMatrix<GLdouble>             _u_knot_vector;
        ColumnMatrix<GLdouble>          _v_knot_vector;
        Matrix<DCoordinate3>            _data_net_to_interpolate;

        GLdouble min_length;
        GLboolean _show;
        TrigoBernsteinSurfPopulation    *_trigo_population;
        TrigoBernsteinSurfPopulation::Individual* best;
        QTimer                          *timer;

        QWidget                         *_parent;

        TrigonometricBernsteinSurface3  *_image_of_data_points;

        QMessageBox                     _message_box;

    public:
        // default/special constructor
        GLWidget(QWidget *parent = 0, const QGLFormat &format = QGL::Rgba | QGL::DepthBuffer | QGL::DoubleBuffer);

        // inherited methods that must be redeclared/redefined
        void initializeGL();
        void paintGL();
        void resizeGL(int w, int h);

        // desctructor
        ~GLWidget();

    public slots:

        void evolve(int checked);
        void animate();

        // public event handlers
        void set_angle_x(int value);
        void set_angle_y(int value);
        void set_angle_z(int value);

        void set_trans_x(double value);
        void set_trans_y(double value);
        void set_trans_z(double value);

        void set_zoom_factor(double value);

        void reset_to_original_view();

        void reset_initialization();

        void initialDataPoints();

        void initialPopulation();

        void set_alpha(double value);
        void set_beta(double value);

        void set_data_point_size_u(int value);
        void set_data_point_size_v(int value);
        void set_individual_number(int value);
        void set_individual_number_to_show(int value);
        void set_data_point_elements_u(int value);
        void set_data_point_elements_v(int value);
        void set_data_point_element_x(double value);
        void set_data_point_element_y(double value);
        void set_data_point_element_z(double value);

        void save_to_file();
        void load_from_file();

        void reset_values();
    };
}
